package pers.cz.javacvdemo.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.*;
import pers.cz.javacvdemo.utils.ReflectUtils;

import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yiyi.su
 * @version 1.0.0
 * @description SetOp
 * @createTime 2022/8/27
 */
@Slf4j
public class SetOp extends Operation {

	private final SetOperations<String, String> set;

	private StringRedisTemplate template;

	public byte[] rawKey(String key) {
		byte[] rawKey = null;
		try {
			rawKey = (byte[]) ReflectUtils.invoke(set, "rawKey", key);
		} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ignored) {
			rawKey = key.getBytes(StandardCharsets.UTF_8);
		}
		return rawKey;
	}

	public SetOp(StringRedisTemplate redisTemplate) {
		this.set = redisTemplate.opsForSet();
	}

	public Long sAdd(String key, Object... values) {
		try {
			String[] array = Arrays.stream(values).map(this::serialize).toArray(String[]::new);
			return set.add(key, array);
		} catch (Exception e) {
			log.warn("sAdd exception, key={},values={}", key, values, e);
		}
		return null;
	}

	public Long sCard(String key) {
		try {
			return set.size(key);
		} catch (Exception e) {
			log.warn("sCard exception, key={}", key, e);
		}
		return null;
	}

	public <T> Set<T> sDiff(String key, Collection<String> otherKeys, Class<T> clz) {
		try {
			Set<String> difference = set.difference(key, otherKeys);
			if (difference == null) {
				return null;
			}
			return difference.stream().map(v -> this.deserialize(v, clz)).collect(Collectors.toSet());
		} catch (Exception e) {
			log.warn("sDiff exception, key={},otherKeys={}", key, otherKeys, e);
		}
		return null;
	}

	public Long sDiffStore(String key, Collection<String> otherKeys, String destKey) {
		try {
			return set.differenceAndStore(key, otherKeys, destKey);
		} catch (Exception e) {
			log.warn("sDiffStore exception, key={},otherKeys={},destKey={}", key, otherKeys, destKey, e);
		}
		return null;
	}

	public <T> Set<T> sInter(String key, Collection<String> otherKeys, Class<T> clz) {
		try {
			Set<String> intersect = set.intersect(key, otherKeys);
			if (intersect == null) {
				return null;
			}
			return intersect.stream().map(v -> this.deserialize(v, clz)).collect(Collectors.toSet());
		} catch (Exception e) {
			log.warn("sInter exception, key={},otherKeys={}", key, otherKeys, e);
		}
		return null;
	}

	public Long sInterStore(String key, Collection<String> otherKeys, String destKey) {
		try {
			return set.intersectAndStore(key, otherKeys, destKey);
		} catch (Exception e) {
			log.warn("sInterStore exception, key={},otherKeys={},destKey={}", key, otherKeys, destKey, e);
		}
		return null;
	}

	public Boolean sIsMember(String key, Object value) {
		try {
			return set.isMember(key, serialize(value));
		} catch (Exception e) {
			log.warn("sIsMember exception, key={},value={}", key, value, e);
		}
		return null;
	}

	public <T> Set<T> sMembers(String key, Class<T> clz) {
		try {
			Set<String> members = set.members(key);
			if (members == null) {
				return null;
			}
			return members.stream().map(v -> this.deserialize(v, clz)).collect(Collectors.toSet());
		} catch (Exception e) {
			log.warn("sMembers exception, key={}", key, e);
		}
		return null;
	}

	public Map<Object, Boolean> sMIsMember(String key, Object... objects) {
		try {
			String[] array = Arrays.stream(objects).map(this::serialize).toArray(String[]::new);
			Map<Object, Boolean> member = set.isMember(key, array);
			if (member == null) {
				return null;
			}
			Map<Object, Boolean> result = new HashMap<>();
			for (int i = 0; i < array.length; i++) {
				result.put(objects[i], member.get(array[i]));
			}
			return result;
		} catch (Exception e) {
			log.warn("sMIsMember exception, key={},objects={}", key, objects, e);
		}
		return null;
	}

	public Boolean sMove(String key, Object value, String destKey) {
		try {
			return set.move(key, serialize(value), destKey);
		} catch (Exception e) {
			log.warn("sMove exception, key={},value={},destKey={}", key, value, destKey, e);
		}
		return null;
	}

	public <T> T sPop(String key, Class<T> clz) {
		try {
			return deserialize(set.pop(key), clz);
		} catch (Exception e) {
			log.warn("sPop exception, key={}", key, e);
		}
		return null;

	}

	public <T> List<T> sPop(String key, long count, Class<T> clz) {
		try {
			List<String> pop = set.pop(key, count);
			if (pop == null) {
				return null;
			}
			return pop.stream().map(v -> this.deserialize(v, clz)).collect(Collectors.toList());
		} catch (Exception e) {
			log.warn("sPop exception, key={},count={}", key, count, e);
		}
		return null;
	}

	public <T> List<T> sRandMember(String key, long count, Class<T> clz) {
		try {
			List<String> tmp = null;
			if (count > 0) {
				Set<String> members = set.distinctRandomMembers(key, count);
				if (members != null) {
					tmp = new ArrayList<>(members);
				}
			} else {
				tmp = set.randomMembers(key, -count);
			}
			if (tmp == null) {
				return null;
			}
			return tmp.stream().map(v -> this.deserialize(v, clz)).collect(Collectors.toList());
		} catch (Exception e) {
			log.warn("sRandMember exception, key={},count={}", key, count, e);
		}
		return null;
	}

	public Long remove(String key, Object... values) {
		try {
			String[] array = Arrays.stream(values).map(this::serialize).toArray(String[]::new);
			return set.remove(key, array);
		} catch (Exception e) {
			log.warn("remove exception, key={},values={}", key, values, e);
		}
		return null;
	}

	public <T> Cursor<T> scan(String key, ScanOptions options, Class<T> clz) {
		try {
			return new ConvertingCursor<>(set.scan(key, options), (s -> this.deserialize(s, clz)));
		} catch (Exception e) {
			log.warn("scan exception, key={},options={}", key, options, e);
		}
		return null;
	}

	public <T> Set<T> sUnion(String key, Collection<String> otherKeys, Class<T> clz) {
		try {
			Set<String> union = set.union(key, otherKeys);
			if (union == null) {
				return null;
			}
			return union.stream().map(s -> this.deserialize(s, clz)).collect(Collectors.toSet());
		} catch (Exception e) {
			log.warn("sUnion exception, key={},otherKeys={}", key, otherKeys, e);
		}
		return null;
	}

	public Long sUnionStore(String key, Collection<String> otherKeys, String destKey) {
		try {
			return set.unionAndStore(key, otherKeys, destKey);
		} catch (Exception e) {
			log.warn("sUnionStore exception, key={},otherKeys={},destKey={}", key, otherKeys, destKey, e);
		}
		return null;
	}

}
